package com.demo.common.plugin.activerecord;


import com.demo.common.plugin.activerecord.directive.*;
import com.jfinal.kit.PathKit;
import com.jfinal.log.Log;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.*;
import com.jfinal.template.Engine;
import com.jfinal.template.Template;

import java.io.File;
import java.util.*;

/**
 * 项目启动时 数据库升级插件
 * @author  杜福忠
 */
public class UpgradeSqlPlugin implements IPlugin {
    private Log log = Log.getLog(UpgradeSqlPlugin.class);
    private ActiveRecordPlugin arp;
    private DbPro db;
    private Config config;
    private Engine engine;
    private String tableName = "upgrade_sql_config_log";
    private String folder = "/upgrade_sql";
    private ArrayList<String> existing = null;
    private boolean immediatelyStop = true;

    public UpgradeSqlPlugin(IDataSourceProvider dataSource) {
        this.arp = new ActiveRecordPlugin(getName(), dataSource);
    }

    @SuppressWarnings("unused")
    public UpgradeSqlPlugin(IDataSourceProvider dataSource, int transactionLevel) {
        this.arp = new ActiveRecordPlugin(getName(), dataSource, transactionLevel);
    }

    private static int count = 0;
    protected synchronized static String getName() {
        count ++;
        return "UpgradeSqlPlugin_" + count;
    }

    @Override
    public boolean stop() {
        db = null;
        config = null;
        engine = null;
        existing = null;
        if (this.arp != null){
            this.arp.stop();
            this.arp = null;
        }
        SqlParaDirective.remove();
        log = null;
        return true;
    }

    @Override
    public boolean start() {
        Objects.requireNonNull(this.arp, "UpgradeSqlPlugin设计上只支持start一次");
        this.arp.start();

        try {
            configDb();

            configSql();
        }catch (Exception e) {
            log.error("升级数据库运行异常：", e);
            return false;
        }
        //执行完升级SQL 立即注销 节约内存
        if (this.immediatelyStop){
            stop();
        }
        return true;
    }

    @SuppressWarnings("unused")
    public UpgradeSqlPlugin setImmediatelyStop(boolean immediatelyStop) {
        this.immediatelyStop = immediatelyStop;
        return this;
    }

    private void configSql() {
        File files = getFolderPath();
        if (! files.isDirectory()){
            return;
        }
        File[] listFiles = files.listFiles();
        if (listFiles == null || listFiles.length == 0) {
            return;
        }
        //名称排序
        Arrays.sort(listFiles);

        for (File file : listFiles) {
            //名称统一小写，避免平台问题
            String sqlName = file.getName().toLowerCase();
            if (file.isFile() && sqlName.endsWith(".sql") && !existing.contains(sqlName)){
                runSql(file, sqlName);
            }
        }
    }

    private File getFolderPath() {
        return new File(PathKit.getRootClassPath() + folder);
    }

    private void runSql(File file, String sqlName) {
        Template template = engine.getTemplate(file.getName());
        log.info("数据库执行升级文件：" + sqlName);

        long t = System.currentTimeMillis();

        final String[] ret = {"ok", null};
        db.tx(() -> {
            try{
                ret[1] = template.renderToString();
                return true;
            }catch (Exception e){
                ret[0] = "fail";
                ret[1] = e.getMessage();
                return false;
            }
        });
        saveRecord(sqlName, ret[0], t, ret[1]);
    }

    protected void saveRecord(String sqlName, String status, long t, String logStr) {
        if (logStr != null) {
            logStr = logStr.trim();
        }
        Record r = new Record();
        r.set("sqlName", sqlName);
        r.set("status", status);
        r.set("runDate", new Date());
        r.set("duration", System.currentTimeMillis() - t);
        r.set("log", logStr);
        db.save(tableName, r);
    }

    private void configDb() {
        this.config = arp.getConfig();
        Objects.requireNonNull(config, "ActiveRecordPlugin 需要先启动");
        this.db = Db.use(config.getName());
        SqlParaDirective.setDb(this.db);

        configEngine();

        try{
            List<Record> list = db.find("SELECT sqlName FROM " + tableName + " WHERE status = 'ok'");
            this.existing = new ArrayList<>(list.size());
            for (Record r : list) {
                this.existing.add(r.getStr("sqlName"));
            }
        }catch (ActiveRecordException e) {
            //建表
            initTable();
            this.existing = new ArrayList<>(0);
        }
    }

    protected void initTable() {
        db.update("CREATE TABLE `" + tableName + "`  ( " +//
                "  `id` int(11) NOT NULL AUTO_INCREMENT, " +//
                "  `sqlName` varchar(255) NOT NULL, " +//
                "  `status` varchar(10) NULL, " +//
                "  `runDate` datetime NULL, " +//
                "  `duration` int(11) NULL, " +//
                "  `log` longtext NULL, " +//
                "  PRIMARY KEY (`id`) " +//
                ") COMMENT='数据库升级记录'");
    }

    private void configEngine() {
        this.engine = config.getSqlKit().getEngine();
        engine.setBaseTemplatePath(folder);
        engine.addDirective("try", TryDirective.class, true);
        engine.addDirective("find", FindDirective.class, true);
        engine.addDirective("findFirst", FindFirstDirective.class, true);
        engine.addDirective("runSql", RunSqlDirective.class, true);
    }

    @SuppressWarnings("unused")
    public void setTableName(String tableName) {
        Objects.requireNonNull(tableName, "tableName 不能为null");
        this.tableName = tableName;
    }

    public void setFolder(String folder) {
        Objects.requireNonNull(folder, "folder 不能为null");
        if (! folder.startsWith("/")) {
            folder = "/" + folder;
        }
        this.folder = folder;
    }

    public DbPro getDb() {
        return db;
    }


}
